import {
  ref,
  Ref,
  defineComponent,
  computed,
  watch,
  nextTick,
  onMounted,
  onUnmounted,
} from 'vue';
import {
  getDropdownProps,
  getEditorEmits,
  useClickOutside,
  useNamespace,
  useCodeListListen,
} from '@ibiz-template/vue3-util';
import './ibiz-dropdown.scss';
import { OnClickOutsideResult } from '@ibiz-template/core';
import { CodeListItem } from '@ibiz-template/runtime';
import { DropDownListEditorController } from '../dropdown-list-editor.controller';

export const IBizDropdown = defineComponent({
  name: 'IBizDropdown',
  props: getDropdownProps<DropDownListEditorController>(),
  emits: getEditorEmits(),
  setup(props, { emit }) {
    const ns = useNamespace('dropdown');
    const c = props.controller;

    const items: Ref<readonly IData[]> = ref([]);

    // 树型数据展开的代码表
    const codeListItems: Ref<IData[]> = ref([]);

    // 是否已加载代码表
    const isLoadedCodeList = ref(false);

    // 是否是树形
    const hasChildren = ref(false);

    // 树数据
    const treeNodes: Ref<IData[]> = ref([]);

    // 是否编辑态
    const isEditable = ref(false);

    // 编辑器Ref
    const editorRef = ref();

    // 值项
    const editorItems = c.model.editorItems;

    // 是否正在加载
    const isLoading = ref(false);

    // 编辑器状态
    let editorState = '';

    // 点击外部
    let funcs: OnClickOutsideResult;

    // 代码项值是否为数值
    const codeItemValueNumber = ref(false);

    // 是否显示表单默认内容
    const showFormDefaultContent = computed(() => {
      if (
        props.controlParams &&
        props.controlParams.editmode === 'hover' &&
        !props.readonly
      ) {
        return true;
      }
      return false;
    });

    // 样式变量
    const cssVars = computed(() => {
      if (c.model.editorType === 'DROPDOWNLIST_100') {
        return ns.cssVarBlock({ width: `100px` });
      }
      return {};
    });

    // 处理树数据
    const handleTreeNodes = (nodes: readonly IData[]) => {
      if (nodes.length === 0) {
        return [];
      }
      const list: IData[] = [];
      nodes.forEach((codeItem: IData) => {
        codeListItems.value.push(codeItem);
        const tempObj: IData = {
          label: codeItem.text,
          value: codeItem.value.toString(),
          color: codeItem?.color,
          textCls: codeItem?.textCls,
          sysImage: codeItem.sysImage,
          disabled: codeItem.disableSelect === true,
          children: [],
        };
        if (codeItem.children && codeItem.children.length > 0) {
          tempObj.children = handleTreeNodes(codeItem.children);
        }
        list.push(tempObj);
      });
      return list;
    };

    // 加载代码表数据与后续操作
    const loadCodeList = async () => {
      if (c.model.appCodeListId) {
        const app = ibiz.hub.getApp(c.context.srfappid);
        const codeListModel = app.codeList.getCodeList(c.model.appCodeListId);
        if (codeListModel) {
          codeItemValueNumber.value =
            codeListModel.codeItemValueNumber || false;
        }
      }
      isLoading.value = true;
      const codeList = await c.loadCodeList(props.data!);
      isLoadedCodeList.value = true;
      items.value = codeList;
      for (let i = 0; i < items.value.length; i++) {
        const _item = items.value[i];
        if (_item.children) {
          hasChildren.value = true;
          treeNodes.value = handleTreeNodes(codeList);
          break;
        }
      }
      isLoading.value = false;
    };

    watch(
      () => props.value,
      async (newVal, oldVal) => {
        if (newVal || newVal === null || newVal === 0) {
          // 值从无到有、并且没搜索过的情况
          if (!isLoadedCodeList.value && oldVal === undefined) {
            await loadCodeList();
          }
        }
      },
      { immediate: true },
    );

    const setEditable = (flag: boolean) => {
      if (flag) {
        isEditable.value = flag;
      } else {
        setTimeout(() => {
          isEditable.value = flag;
        }, 100);
      }
    };

    /**
     * 根据值获取代码表
     *
     * @author zk
     * @date 2024-01-17 03:01:53
     * @param {string} value
     * @return {*}
     */
    const getCodeListItemByValue = (value: string | number) => {
      const list = hasChildren.value ? codeListItems.value : items.value;
      return list.find(item => item.value === value);
    };

    // 当前值
    const curValue = computed({
      get() {
        if (!isLoadedCodeList.value) {
          return c.multiple ? [] : '';
        }
        // 单选才考虑值项
        if (editorItems && editorItems.length > 0 && !c.multiple) {
          return props.data[editorItems[0].id!]?.toString();
        }
        if (props.value && typeof props.value === 'string') {
          return c!.multiple
            ? props.value?.toString().split(',')
            : props.value.toString();
        }
        if (props.value && Array.isArray(props.value)) {
          return c!.multiple ? props.value : props.value.toString();
        }
        return props.value?.toString();
      },
      set(select: string | Array<string> | undefined) {
        if (Array.isArray(select)) {
          let selectArr = null;
          if (select.length === 0) {
            selectArr = null;
          } else if (c.model.valueType === 'SIMPLES') {
            selectArr = select;
          } else {
            selectArr = select.join(',');
          }
          emit('change', selectArr);
        } else if (editorItems && editorItems.length > 0) {
          // 有值项 默认抛标题 值项抛实际value
          emit(
            'change',
            codeItemValueNumber.value ? Number(select) : select,
            editorItems[0].id!,
          );
          const selectItem = getCodeListItemByValue(
            (codeItemValueNumber.value ? Number(select) : select)!,
          );
          if (selectItem) {
            emit('change', selectItem.text);
          }
        } else {
          emit('change', codeItemValueNumber.value ? Number(select) : select);
        }
        setEditable(false);
      },
    });

    const valueText = computed(() => {
      const valueArr = Array.isArray(curValue.value)
        ? curValue.value
        : [curValue.value];

      const list = hasChildren.value ? codeListItems.value : items.value;
      const textArr: string[] = [];
      valueArr.forEach(item => {
        list.forEach(codeItem => {
          if (
            codeItemValueNumber.value
              ? codeItem.value === Number(item)
              : codeItem.value === item
          ) {
            textArr.push(codeItem.text);
          }
        });
      });
      return textArr.join(',');
    });

    watch(
      valueText,
      (newVal, oldVal) => {
        if (newVal !== oldVal) {
          emit('infoTextChange', newVal);
        }
      },
      { immediate: true },
    );

    // 自动聚焦
    watch(editorRef, newVal => {
      if (props.autoFocus && newVal && newVal.focus) {
        newVal.focus();
      }
    });
    const onFocus = (e: IData) => {
      editorState = 'focus';
      emit('focus', e);
      setEditable(true);
    };

    const onBlur = (e: IData) => {
      editorState = 'blur';
      emit('blur', e);
      setEditable(false);
    };

    // 处理点击键盘
    const handleKeyUp = (e: KeyboardEvent) => {
      if (e && e.code === 'Enter') {
        emit('enter', e);
      }
    };

    /**
     * 自定义节点样式
     * @param data 数据对象
     * @param _node 节点对象
     * @returns
     */
    const customNodeClass = (data: IData, _node: Node) => {
      return data.children.length ? ns.e('branch-node') : null;
    };

    // 根据text获取对应代码表项
    const getCodeListItem = (text: string) => {
      const list = hasChildren.value ? codeListItems.value : items.value;
      return list.find(item => item.text === text);
    };

    // 下拉框出现/隐藏时触发
    const onVisibleChange = async (visible: boolean) => {
      if (visible && !isLoadedCodeList.value) {
        // 下拉框出现时如果没有加载过代码表去加载
        await loadCodeList();
        // 树型的情况下，得自动聚焦, 如果已经失焦就不要focus了
        if (hasChildren.value && editorRef.value && editorState !== 'outside') {
          nextTick(() => {
            editorRef.value.focus();
          });
        }
      }
    };

    const fn = (data: CodeListItem[] | undefined) => {
      if (data) {
        items.value = data;
      }
    };

    useCodeListListen(c.model.appCodeListId, c.context.srfappid, fn);

    onMounted(() => {
      if (editorRef.value) {
        funcs = useClickOutside(editorRef, async _evt => {
          editorState = 'outside';
        });
      }
    });

    onUnmounted(() => {
      if (funcs && funcs.stop) {
        funcs.stop();
      }
    });

    return {
      ns,
      c,
      curValue,
      items,
      customNodeClass,
      valueText,
      hasChildren,
      onBlur,
      onFocus,
      editorRef,
      treeNodes,
      cssVars,
      handleKeyUp,
      getCodeListItem,
      isEditable,
      setEditable,
      showFormDefaultContent,
      onVisibleChange,
      isLoading,
    };
  },

  render() {
    const overflowMode =
      this.c.editorParams.overflowMode || ibiz.config.pickerEditor.overflowMode;
    const isEllipsis = overflowMode === 'ellipsis';

    // 编辑态内容
    const editContent = this.hasChildren ? (
      <el-tree-select
        ref='editorRef'
        v-model={this.curValue}
        clearable
        class={[this.ns.b('select')]}
        filterable={true}
        teleported={!this.showFormDefaultContent}
        data={this.treeNodes}
        check-strictly
        render-after-expand={true}
        multiple={this.c.multiple}
        placeholder={this.c.placeHolder ? this.c.placeHolder : ' '}
        disabled={this.disabled}
        loading={this.isLoading}
        fit-input-width={isEllipsis}
        onBlur={this.onBlur}
        onFocus={this.onFocus}
        onKeyup={this.handleKeyUp}
        onVisibleChange={this.onVisibleChange}
        props={{
          class: this.customNodeClass,
        }}
        {...this.$attrs}
      >
        {{
          default: (node: IData) => {
            const data = node.data || {};
            const label = data.label || '';
            return <span title={isEllipsis ? label : ''}>{label}</span>;
          },
        }}
      </el-tree-select>
    ) : (
      <el-select
        ref='editorRef'
        v-model={this.curValue}
        clearable
        class={[this.ns.b('select')]}
        filterable={true}
        teleported={!this.showFormDefaultContent}
        multiple={this.c.multiple}
        allow-create={!this.c.forceSelection}
        placeholder={this.c.placeHolder ? this.c.placeHolder : ' '}
        disabled={this.disabled}
        loading={this.isLoading}
        fit-input-width={isEllipsis}
        onBlur={this.onBlur}
        onFocus={this.onFocus}
        onKeyup={this.handleKeyUp}
        onVisibleChange={this.onVisibleChange}
        {...this.$attrs}
      >
        {this.items.map(item => {
          return (
            <el-option
              key={item.value}
              value={item.value.toString()}
              label={item.text}
              disabled={item.disableSelect === true}
              style={
                item.bkcolor
                  ? this.ns.cssVarBlock({
                      'select-option-item-bkcolor': `${item.bkcolor}`,
                    })
                  : ''
              }
              class={[item.cls ? item.cls : null]}
              title={isEllipsis ? item.text : ''}
            >
              {{
                default: () => {
                  return (
                    <div
                      class={[
                        this.ns.b('select-option-content'),
                        item.textCls ? item.textCls : null,
                      ]}
                      style={
                        item.color
                          ? this.ns.cssVarBlock({
                              'select-option-item-color': `${item.color}`,
                            })
                          : ''
                      }
                    >
                      {item.sysImage && (
                        <iBizIcon icon={item.sysImage}></iBizIcon>
                      )}
                      <span
                        class={[
                          isEllipsis &&
                            this.ns.be('select-option-content', 'text'),
                        ]}
                      >
                        {item.text}
                      </span>
                    </div>
                  );
                },
              }}
            </el-option>
          );
        })}
      </el-select>
    );

    // 只读态内容
    const readonlyContent = this.valueText.split(',').map(text => {
      const codeListItem = this.getCodeListItem(text);
      return (
        <span
          class={[this.ns.b('readonly-text-item'), codeListItem?.textCls]}
          style={
            codeListItem?.color
              ? this.ns.cssVarBlock({
                  'readonly-text-item-color': `${codeListItem.color}`,
                })
              : ''
          }
        >
          {codeListItem?.sysImage && (
            <iBizIcon icon={codeListItem?.sysImage}></iBizIcon>
          )}
          {text}
        </span>
      );
    });

    // 表单默认内容
    const formDefaultContent = (
      <div
        class={[
          this.ns.b('form-default-content'),
          this.ns.is('multiple', this.c.multiple),
        ]}
      >
        {this.valueText
          ? this.valueText.split(',').map(text => {
              return <span class={this.ns.b('content-item')}>{text}</span>;
            })
          : '-'}
      </div>
    );

    return (
      <div
        class={[
          this.ns.b(),
          this.disabled ? this.ns.m('disabled') : '',
          this.readonly ? this.ns.m('readonly') : '',
          this.ns.is('editable', this.isEditable),
          this.ns.is('show-default', this.showFormDefaultContent),
        ]}
        style={this.cssVars}
      >
        {this.showFormDefaultContent && formDefaultContent}
        {this.readonly ? readonlyContent : editContent}
      </div>
    );
  },
});
